/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

 /***************************************************************************
 * Name: system_property_impl.h
 *
 * Purpose: system property implementation
 *
 * Developer:
 *   wen.gu , 2019-10-05
 *
 * TODO:
 *
 ***************************************************************************/

 /******************************************************************************
 **    INCLUDES
 ******************************************************************************/
#ifndef __PANDA_SYS_INTERNAL_SYSTEM_PROPERTY_IMPL_H__
#define __PANDA_SYS_INTERNAL_SYSTEM_PROPERTY_IMPL_H__

#include <atomic>

#include "panda/core/types.h"
#include "panda/core/errors.h"


/******************************************************************************
 **    MACROS
 ******************************************************************************/
/** the max length of property value */
#define PROPERTY_VALUE_MAX_SIZE 92

/** the name of property shared memory */
#define PROPERTY_SHM_NAME "__panda_property__"


/** the max size of property shared memory area */
#define PROPERTY_AREA_MAX_SIZE (1024 * 256)


 /******************************************************************************
 **    TYPE DEFINITIONS
 ******************************************************************************/
namespace panda
{
namespace core
{
class SharedMemory;
} /** namespace core */

namespace sys
{
namespace internal_impl
{
/**map:  key - value */
using PropertyList = std::map<std::string, std::string>;


struct TrieNode
{
    uint32_t nameLen = 0;
    std::atomic_uint_least32_t prop; /** the really property value node( the leaf of trie) */
    std::atomic_uint_least32_t left; /** the node which domain name < current domain name */
    std::atomic_uint_least32_t right; /** the node which domain name > current domain name */
    std::atomic_uint_least32_t children; /** the really child node of current trie node,
                                              *  target domain name == current domain name.
                                              */
    char* name = nullptr;

};

struct PropNode
{
    uint32_t nameLen = 0;
    uint8_t value[PROPERTY_VALUE_MAX_SIZE];
    char* name = nullptr;
};


 /******************************************************************************
 **    CLASSES/FUNCTIONS DEFINITIONS
 ******************************************************************************/


class CORE_CLASS PropTrie
{
public:
    /**
     * true: can write and read property
     * false: read only for all property
     */
    static PropTrie* loadPropertyArea(const char* filename, bool isOwner = false);

    static void unloadPropertyArea(PropTrie** pa);

public:
    PropTrie(const uint32_t magic, const uint32_t version);

    const PropNode* find(const char* name);
    bool add(const char* name, unsigned int namelen, 
             const char* value, unsigned int valuelen);

    bool foreach(void (*propFunc)(const PropNode* pn, void* opaque), void* opaque);

    std::atomic_uint_least32_t* serial();
    uint32_t magic() const;

    uint32_t version() const;

protected:
     ~PropTrie();
private:
    uint8_t* allocateObj(const size_t size, uint_least32_t* const off);
    TrieNode* newTrieNode(const char* name, uint32_t namelen, uint_least32_t* const off);

    PropNode* newPropNode(const char* name, uint32_t namelen, 
                          const char* value, uint32_t valuelen, uint_least32_t* const off);

    uint8_t* toTrieNodeObj(uint_least32_t off);
    TrieNode* toTrieNode(std::atomic_uint_least32_t* off_p);
    PropNode* toPropNode(std::atomic_uint_least32_t* off_p);

    TrieNode* rootTrieNode();

    TrieNode* findTrieNode(TrieNode* const bt, const char* name, 
                           uint32_t namelen, bool alloc_if_needed);

    const PropNode* findPropertyNode(TrieNode* const trie, const char* name, 
                                 uint32_t namelen, const char* value, 
                                 uint32_t valuelen, bool alloc_if_needed);

    bool foreachProperty(TrieNode* const trie, 
                         void (*propFunc)(const PropNode* pi, void* opaque),
                         void* opaque);
private:
    static uint32_t mPropAreaSize;
    static uint32_t mPropAreaDataSize;

    uint32_t mBufferUsed = 0;
    std::atomic_uint_least32_t mSerial;
    uint32_t mMagicNum;
    uint32_t mVersion;
    uint32_t mReserved[28];
    uint8_t* mData = nullptr;
    panda::core::SharedMemory* mShm = nullptr;
};



class CORE_CLASS SystemProperty
{
public:
    using PandaErrc = panda::core::PandaErrc;
public: 
    static SystemProperty& instance();
    ~SystemProperty();

protected:
    SystemProperty(); 
public:  
    /** 
     * true: can write and read property
     * false: read only for all property
     */
    bool reinit(bool isOwner);

    bool isInited();

    const PropNode* find(const std::string& key);
    PandaErrc get(const std::string& key, std::string& val);
    PandaErrc add(const std::string& key, const std::string& val);
    PandaErrc update(PropNode* pn, const std::string& val);


    PandaErrc list(PropertyList& proplist);

    bool foreach(void (*propFunc)(const PropNode* pn, void* opaque), void* opaque);
private:
    bool mIsOwner = false;
    PropTrie* mTrie = nullptr;
};



inline bool _SystemPropertyGet(const std::string& key, std::string& val)
{
    return bool(SystemProperty::instance().get(key, val));
    
}

inline bool _SystemPropertySet(const std::string& key, const std::string& val)
{
    SystemProperty& sysProp = SystemProperty::instance();
    const PropNode* pn = sysProp.find(key);

    if (pn)
    {
        return bool(sysProp.update((PropNode*)pn, val));
    }

    return bool(sysProp.add(key, val));
}

inline const PropNode* _SystemPropertyFind(const std::string& key)
{
    return SystemProperty::instance().find(key);
}

inline bool _SystemPropertyUpdate(PropNode* pn, const std::string& val)
{
    return bool(SystemProperty::instance().update(pn, val));
}


} /** namespace internal_impl */
} /** namespace sys */
} /** namespace panda*/

#endif /** !__PANDA_SYS_INTERNAL_SYSTEM_PROPERTY_IMPL_H__ */
